package com.quectel.cms.controller.sys;


import cn.hutool.crypto.digest.BCrypt;
import com.quectel.cms.controller.BaseController;
import com.quectel.cms.vo.sys.SysTenantOperateVo;
import com.quectel.cms.vo.sys.TenantInitRetryReq;
import com.quectel.constant.global.SystemConstants;
import com.quectel.constant.global.annotation.RequirePermissions;
import com.quectel.constant.global.annotation.RequireTenants;
import com.quectel.constant.sys.SysConstants;
import com.quectel.constant.sys.SysMenuConstants;
import com.quectel.constant.sys.SysTenantConstants;
import com.quectel.core.module.partybuilding.dto.PartyOrganizationDto;
import com.quectel.core.module.partybuilding.service.PartyOrganizationService;
import com.quectel.sys.module.sys.dto.*;
import com.quectel.sys.module.sys.service.SysGroupService;
import com.quectel.sys.module.sys.service.SysRoleService;
import com.quectel.sys.module.sys.service.SysTenantService;
import com.quectel.sys.module.sys.service.SysUserService;
import com.quectel.util.common.Page;
import com.quectel.util.common.RandomUtils;
import com.quectel.util.common.Request;
import com.quectel.util.common.Response;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author rananxin
 * @date 2021-09-01 09:31:46
 */
@RestController
@RequestMapping("sysTenant")
@Tag(name = "SysTenantController", description = "租户相关api")
public class SysTenantController extends BaseController {

    @DubboReference
    private SysTenantService sysTenantService;
    @DubboReference
    private SysGroupService sysGroupService;
    @DubboReference
    private SysRoleService sysRoleService;
    @DubboReference
    private SysUserService sysUserService;
    @DubboReference
    private PartyOrganizationService partyOrganizationService;

    @PostMapping("initRetry")
    @RequirePermissions(values = "sysTenant:edit")
    @Operation(summary = "租户初始化资源失败重试")
    @RequireTenants(values = SysConstants.SYS_TOP_TENANT_CODE)
    public Response<Object> initRetry(@RequestBody @Validated TenantInitRetryReq retryReq){
        // 目前仅有党组织资源需要重试
        initResource(retryReq.getTenantId(), SysTenantConstants.InitResourceEnum.PARTY_ORG_RESOURCE.getType());
        return Response.ok();
    }

    /**
     * 列表
     */
    @GetMapping("list")
    @RequirePermissions(values = "sysTenant:list")
    @Operation(summary = "租户分页查询列表")
    @RequireTenants(values = SysConstants.SYS_TOP_TENANT_CODE)
    public Response<Page<SysTenantDto>> list(
            @Parameter(description = "从1开始的页码数") @RequestParam Integer pageNo,
            @Parameter(description = "每页多少条") @RequestParam Integer pageSize
    ) {
        //查询列表数据
        Request request = Request.configParams(Request.parseParams(httpServletRequest)).like("name");

        super.fillAuthorityData(request.getParams());
        int total = sysTenantService.queryTotal(request.getParams());

        List<SysTenantDto> list = null;
        if (total > 0) {
            list = sysTenantService.queryList(request.getParams());
        } else {
            list = new ArrayList<>();
        }

        Page<SysTenantDto> page = new Page<>(request.getPageNo(), request.getPageSize(), total, list);
        return Response.<Page<SysTenantDto>>ok().wrap(page);
    }


    /**
     * 信息
     */
    @GetMapping("info/{id}")
    @RequirePermissions(values = "sysTenant:info")
    @Operation(summary = "根据id查询系统租户单条记录")
    @RequireTenants(values = SysConstants.SYS_TOP_TENANT_CODE)
    public Response<SysTenantOperateVo> info(@PathVariable("id") Long id) {

        Map<String, Object> params = new HashMap<>();
        super.fillAuthorityData(params);
        params.put(SystemConstants.ENTITY_ID_FLAG, String.valueOf(id));
        if (sysTenantService.queryTotal(params) <= 0) {
            return Response.error(SystemConstants.ILLEGAL_MSG);
        }

        SysTenantOperateVo result = new SysTenantOperateVo();
        SysTenantDto sysTenantDto = sysTenantService.selectById(id);
        if (sysTenantDto != null) {
            result.setSysTenant(sysTenantDto);
            SysUserDto adminUser = sysTenantDto.getAdminUser();
            if (adminUser != null) {
                result.setSysUser(adminUser);
                List<SysRoleDto> userRoleList = adminUser.getUserRoleList();
                if (CollectionUtils.isNotEmpty(userRoleList)) {
                    Long[] roleIds = userRoleList.stream().map(SysRoleDto::getId).toArray(Long[]::new);
                    result.setRoleIds(roleIds);
                }
            }

            // 获取资源初始化状态
            result.setInitResources(initResourceStatus(id));
        }

        return Response.<SysTenantOperateVo>ok().wrap(result);
    }

    /**
     * 保存
     */
    @PostMapping("save")
    @RequirePermissions(values = "sysTenant:edit")
    @Operation(summary = "保存系统租户单条记录")
    @RequireTenants(values = SysConstants.SYS_TOP_TENANT_CODE)
    public Response<Object> save(@RequestBody @Validated SysTenantOperateVo sysAddTenantVo) {

        SysTenantDto oldTenant = sysTenantService
                .selectOneByCode(sysAddTenantVo.getSysTenant().getCode());
        if (oldTenant != null) {
            return Response.error("租户编码已存在");
        }

        SysUserDto oldUser = sysUserService.selectByAccount(sysAddTenantVo.getSysUser().getAccount());
        if (oldUser != null) {
            return Response.error("已存在该管理员账号，请重新设置");
        }

        fillSaveCommonData(sysAddTenantVo.getSysTenant());
        List<SysRoleDto> appPackages = Arrays.stream(sysAddTenantVo.getRoleIds())
                .map(id -> sysRoleService.selectById(id))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        // 非顶级租户 包含系统级菜单不允许创建
        if (!SysConstants.SYS_TOP_TENANT_CODE.equals(sysAddTenantVo.getSysTenant().getCode())) {
            for (SysRoleDto appPackage : appPackages) {
                List<SysMenuDto> roleMenuList = appPackage.getRoleMenuList();
                if (CollectionUtils.isNotEmpty(roleMenuList)) {
                    boolean b = roleMenuList.parallelStream()
                            .filter(SysMenuDto::isChecked)
                            .anyMatch(o -> SysMenuConstants.IsSystemLevelEnum.YES.getLevel()
                                    .equals(o.getIsSystemLevel()));
                    if (b) {
                        return Response.error("创建失败，检查应用包是否关联了系统级菜单");
                    }
                }
            }
        }

        SysUserDto sysUser = sysAddTenantVo.getSysUser();
        sysUser.setUserRoleList(appPackages);

        String randomPassword = RandomUtils.genStrongPassword(6, 8);
        sysUser.setPassword(BCrypt.hashpw(randomPassword, BCrypt.gensalt()));

        Long tenantId = sysTenantService.save(sysAddTenantVo.getSysTenant(), sysUser);

        initResource(tenantId, SysTenantConstants.InitResourceEnum.PARTY_ORG_RESOURCE.getType());

        return Response.ok().wrap("管理员初始密码为: " + randomPassword);
    }

    /**
     * 初始化租户资源是否成功查询
     *
     * @param tenantId      党组织id
     * @see SysTenantConstants.InitResourceEnum
     */
    private List<SysTenantOperateVo.InitResourceCls> initResourceStatus(Long tenantId) {
        SysTenantDto sysTenantDto = sysTenantService.selectById(tenantId);
        if (Objects.isNull(sysTenantDto)) {
            logger.warn("Tenant is null, init failure.");
            return new ArrayList<>();
        }
        List<SysTenantOperateVo.InitResourceCls> result = new ArrayList<>();
        for (SysTenantConstants.InitResourceEnum initResourceEnum : SysTenantConstants.InitResourceEnum.values()) {
            // 默认成功
            SysTenantOperateVo.InitResourceCls initResourceCls = SysTenantOperateVo.InitResourceCls.builder()
                    .tenantId(sysTenantDto.getId())
                    .tenantName(sysTenantDto.getName())
                    .resourceType(initResourceEnum.getType())
                    .resourceName(initResourceEnum.getName())
                    .initStatus(SysTenantConstants.InitStatusEnum.INIT_SUCCESS.getInitStatus())
                    .build();
            if (SysTenantConstants.InitResourceEnum.PARTY_ORG_RESOURCE.getType().equals(initResourceEnum.getType())) {
                PartyOrganizationDto partyOrganizationDto = partyOrganizationService.selectByCode(SysConstants.ROOT_PARTY_ORG_CODE, tenantId);
                if (partyOrganizationDto == null) {
                    // 未初始化成功
                    initResourceCls.setInitStatus(SysTenantConstants.InitStatusEnum.INIT_FAILURE.getInitStatus());
                }
            }
            result.add(initResourceCls);
        }
        return result;

    }

    /**
     * 初始化租户资源
     *
     * @param tenantId      党组织id
     * @param resourceTypes 资源类型集合
     * @see SysTenantConstants.InitResourceEnum
     */
    private void initResource(Long tenantId, Byte... resourceTypes) {
        SysTenantDto sysTenantDto = sysTenantService.selectById(tenantId);
        if (Objects.isNull(sysTenantDto)) {
            logger.warn("Tenant is null, init failure.");
            return ;
        }
        for (Byte resourceType : resourceTypes) {
            try {
                if (SysTenantConstants.InitResourceEnum.PARTY_ORG_RESOURCE.getType().equals(resourceType)) {
                    PartyOrganizationDto partyOrganizationDto = partyOrganizationService.selectByCode(SysConstants.ROOT_PARTY_ORG_CODE, tenantId);
                    if (partyOrganizationDto == null) {
                        // 初始化党组织资源
                        partyOrganizationDto = new PartyOrganizationDto();
                        fillSaveCommonData(partyOrganizationDto);
                        partyOrganizationDto.setTenantId(sysTenantDto.getId());
                        partyOrganizationDto.setParentId(SystemConstants.TREE_DEFAULT_PARENT_ID);
                        partyOrganizationDto.setCode(SysConstants.ROOT_PARTY_ORG_CODE);
                        partyOrganizationDto.setName(SysConstants.ROOT_PARTY_ORG_NAME);
                        Long partyId = partyOrganizationService.save(partyOrganizationDto);
                        partyOrganizationDto.setId(partyId);
                    }
                    // 超级组对顶级党组组进行关联
                    SysGroupDto sysGroupDto = sysGroupService.selectByCode(SysConstants.SYS_TOP_GROUP_CODE, tenantId);
                    if (sysGroupDto != null) {
                        SysGroupDto u = new SysGroupDto();
                        u.setId(sysGroupDto.getId());
                        u.setIsPartyManager(SystemConstants.YES);
                        u.setPartyId(partyOrganizationDto.getId());
                        sysGroupService.updateById(u);
                    }
                }
            } catch (Exception e) {
                logger.warn("Tenant resources init error. Resource type is {}", resourceType);
            }
        }

    }

    /**
     * 修改
     */
    @PostMapping("update")
    @RequirePermissions(values = "sysTenant:edit")
    @Operation(summary = "修改系统租户单条记录")
    @RequireTenants(values = SysConstants.SYS_TOP_TENANT_CODE)
    public Response<Object> update(@RequestBody @Validated SysTenantOperateVo sysAddTenantVo) {

        SysTenantDto sysTenant = sysAddTenantVo.getSysTenant();

        Map<String, Object> params = new HashMap<>();
        super.fillAuthorityData(params);
        params.put(SystemConstants.ENTITY_ID_FLAG, String.valueOf(sysTenant.getId()));
        if (sysTenantService.queryTotal(params) <= 0) {
            return Response.error(SystemConstants.ILLEGAL_MSG);
        }
        //填充基础数据
        fillEditCommonData(sysTenant);

        List<SysRoleDto> appPackages = Arrays.stream(sysAddTenantVo.getRoleIds())
                .map(id -> sysRoleService.selectById(id))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        // 非顶级租户 包含系统级菜单不允许更新
        if (!SysConstants.SYS_TOP_TENANT_CODE.equals(sysAddTenantVo.getSysTenant().getCode())) {
            for (SysRoleDto appPackage : appPackages) {
                List<SysMenuDto> roleMenuList = appPackage.getRoleMenuList();
                if (CollectionUtils.isNotEmpty(roleMenuList)) {
                    boolean b = roleMenuList.parallelStream()
                            .filter(SysMenuDto::isChecked)
                            .anyMatch(o -> SysMenuConstants.IsSystemLevelEnum.YES.getLevel()
                                    .equals(o.getIsSystemLevel()));
                    if (b) {
                        return Response.error("更新失败，检查应用包是否关联了系统级菜单");
                    }
                }
            }
        }

        SysUserDto sysUser = sysAddTenantVo.getSysUser();
        sysUser.setUserRoleList(appPackages);

        sysTenantService.updateById(sysTenant, sysUser);

        return Response.ok().wrap("操作成功");
    }

    /**
     * 删除
     */
    @PostMapping("delete")
    @RequirePermissions(values = "sysTenant:delete")
    @Operation(summary = "批量删除系统租户单条记录")
    @RequireTenants(values = SysConstants.SYS_TOP_TENANT_CODE)
    public Response<Object> delete(@RequestBody Long[] ids) {


        List<SysTenantDto> list = new ArrayList<>();

        Map<String, Object> params = new HashMap<>();
        super.fillAuthorityData(params);

        for (Long id : ids) {

            params.remove(SystemConstants.ENTITY_ID_FLAG);
            params.put(SystemConstants.ENTITY_ID_FLAG, String.valueOf(id));

            List<SysTenantDto> sysTenantDtos = sysTenantService.queryList(params);
            if (sysTenantDtos == null || sysTenantDtos.size() <= 0) {
                return Response.error(SystemConstants.ILLEGAL_MSG);
            }
            SysTenantDto sysTenantDto = sysTenantDtos.get(0);
            if (SysConstants.SYS_TOP_TENANT_CODE.equals(sysTenantDto.getCode())) {
                // 系统顶级租户不可删除
                continue;
            }
            list.add(sysTenantDto);

        }

        sysTenantService.deleteBatch(list.stream()
                .map(SysTenantDto::getId)
                .toArray(Long[]::new));

        return Response.ok();
    }
}
